package bblrtc

import (
	"sync"
)

type Router struct {
	peers  map[string]*Peer
	peers2 map[string]*Peer
	rtc_ss map[string]*RTCSession
	lock   sync.Mutex
	server *IMGatewayServer
}

func new_router(server *IMGatewayServer) *Router {
	return &Router{peers: make(map[string]*Peer), peers2: make(map[string]*Peer), rtc_ss: make(map[string]*RTCSession), server: server}
}

func (r *Router) Register(member, deviceId, node string) (*Peer, int) {
	user := UDB.FindUser(member)
	if user == nil {
		return nil, ERR_USER_NOT_EXIST
	}
	ss := r.FindPeerWithMember(member)
	if ss == nil {
		ss = new_peer(user, deviceId, node, r)
		r.AddPeer(ss)
	}
	ss.DeviceId = deviceId
	ss.NodeId = node
	return ss, OK
}
func (r *Router) Unregister(sid string) int {
	if sid == "" {
		return ERR_SID
	}
	ss := r.FindPeerWithSid(sid)
	if ss != nil {
		ss.Close()
	}
	r.RemovePeer(sid)
	return OK
}

// func (r *Router) Invite(sid string, msg *Message) (*Session, int) {
// 	ss := r.FindSessionWithSid(sid)
// 	if ss == nil {
// 		return nil, ERR_SNF
// 	}
// 	if ss.IsCalling() {
// 		ss.rtcsession.close()
// 	}
// 	cs := r.FindSessionWithMember(msg.To)
// 	if cs == nil {
// 		return nil, ERR_CALLEE_NOT_EXIST
// 	}
// 	if !cs.IsOnline() {
// 		cs.Close()
// 		return nil, ERR_CALLEE_OFFLINE
// 	}
// 	rtc := new_rtc_session(ss, cs)
// 	msg.From = ss.Member
// 	return ss, rtc.offer(msg)
// }

func (r *Router) Offer(sid string, msg *Message) (*Peer, int) {
	ss := r.FindPeerWithSid(sid)
	if ss == nil {
		return nil, ERR_SNF
	}
	if ss.IsCalling() {
		ss.rtcsession.close()
	}
	cs := r.FindPeerWithMember(msg.To)
	if cs == nil {
		return nil, ERR_CALLEE_NOT_EXIST
	}
	if !cs.IsOnline() {
		cs.Close()
		return nil, ERR_CALLEE_OFFLINE
	}
	rtc := new_rtc_session(ss, cs)
	msg.From = ss.Member
	return ss, rtc.offer(msg)
}

func (r *Router) Candidate(sid string, msg *Message) (*Peer, int) {
	ss := r.FindPeerWithSid(sid)
	if ss == nil || !ss.IsCalling() {
		return nil, ERR_CALLING_NOT_EXIST
	}

	rtc := r.FindRTCSession(msg.CallerSID)
	if rtc == nil {
		return nil, ERR_CALLING_NOT_EXIST
	}
	msg.From = ss.Member
	return ss, rtc.candidate(msg)
}

func (r *Router) Answer(sid string, msg *Message) (*Peer, int) {
	ss := r.FindPeerWithSid(sid)
	if ss == nil || !ss.IsCalling() {
		return nil, ERR_CALLING_NOT_EXIST
	}

	rtc := r.FindRTCSession(msg.CallerSID)
	if rtc == nil {
		return nil, ERR_CALLING_NOT_EXIST
	}
	msg.From = ss.Member
	return ss, rtc.answer(msg)
}

func (r *Router) Bye(sid string, msg *Message) (*Peer, int) {
	ss := r.FindPeerWithSid(sid)
	if ss == nil {
		return nil, ERR_CALLING_NOT_EXIST
	}

	rtc := r.FindRTCSession(msg.CallerSID)
	if rtc == nil {
		return nil, ERR_CALLING_NOT_EXIST
	}
	msg.From = ss.Member
	rtc.bye(msg)

	return ss, OK
}

// func (r *Router) Ack(sid string, msg *Message) (*Session, int) {
// 	ss := r.FindSessionWithSid(sid)
// 	if ss == nil {
// 		return nil, ERR_CALLING_NOT_EXIST
// 	}

// 	rtc := r.FindRTCSession(msg.CallerSID)
// 	if rtc == nil {
// 		return nil, ERR_CALLING_NOT_EXIST
// 	}
// 	msg.From = ss.Member
// 	rtc.bye(msg)

// 	return ss, OK
// }

func (r *Router) AddPeer(peer *Peer) {
	r.lock.Lock()
	r.peers2[peer.Member] = peer
	r.peers[peer.Sid] = peer
	r.lock.Unlock()
}

func (r *Router) RemovePeer(sid string) {
	r.lock.Lock()
	if ss, found := r.peers[sid]; found {
		delete(r.peers2, ss.Member)
	}
	delete(r.peers, sid)
	r.lock.Unlock()
}

func (r *Router) FindPeerWithSid(sid string) *Peer {
	r.lock.Lock()
	if ss, found := r.peers[sid]; found {
		r.lock.Unlock()
		return ss
	} else {
		r.lock.Unlock()
		return nil
	}
}

func (r *Router) FindPeerWithMember(member string) *Peer {
	r.lock.Lock()
	if ss, found := r.peers2[member]; found {
		r.lock.Unlock()
		return ss
	} else {
		r.lock.Unlock()
		return nil
	}
}

func (r *Router) AddRTCSession(session *RTCSession) {
	r.lock.Lock()
	r.rtc_ss[*session.Sid] = session
	r.lock.Unlock()
}

func (r *Router) RemoveRTCSession(sid string) {
	r.lock.Lock()
	delete(r.rtc_ss, sid)
	r.lock.Unlock()
}

func (r *Router) FindRTCSession(sid string) *RTCSession {
	r.lock.Lock()
	if ss, found := r.rtc_ss[sid]; found {
		r.lock.Unlock()
		return ss
	} else {
		r.lock.Unlock()
		return nil
	}
}
